package top.xia17.plugins.fast.user.service.impl;

import cn.hutool.core.util.StrUtil;
import top.xia17.plugins.fast.common.database.jpa.extension.JpaDtoTxServiceImpl;
import top.xia17.plugins.fast.common.exceptions.BadRequestException;
import top.xia17.plugins.fast.common.utils.TreeUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import top.xia17.plugins.fast.user.config.UserProperties;
import top.xia17.plugins.fast.user.dao.entity.Role;
import top.xia17.plugins.fast.user.dao.entity.User;
import top.xia17.plugins.fast.user.dao.repository.RoleRepository;
import top.xia17.plugins.fast.user.dao.repository.UserRepository;
import top.xia17.plugins.fast.user.ds.UserDDS;
import top.xia17.plugins.fast.user.model.dto.UserDto;
import top.xia17.plugins.fast.user.model.mapstruct.UserMapstruct;
import top.xia17.plugins.fast.user.model.resp.MenuVo;
import top.xia17.plugins.fast.user.password.PasswordGeneratorManager;
import top.xia17.plugins.fast.user.service.MenuService;
import top.xia17.plugins.fast.user.service.UserService;

import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 用户service
 *
 * @author xia17
 * @since 2021/7/15 14:06
 */
@Service
@RequiredArgsConstructor
@UserDDS
public class UserServiceImpl extends JpaDtoTxServiceImpl<User, UserDto, Long, UserRepository, UserMapstruct> implements UserService {


    private final PasswordGeneratorManager passwordGeneratorManager;
    private final PasswordEncoder passwordEncoder;
    private final MenuService menuService;
    private final RoleRepository roleRepository;
    private final UserProperties userProperties;


    @Override
    public void createBefore(User entity) {
        // username
        if (baseRepository.countAllByUsername(entity.getUsername()) > 0) {
            throw new BadRequestException("用户名已存在!");
        }
        // phone
        if (userProperties.getEmailUnique() && StrUtil.isNotBlank(entity.getPhone()) && baseRepository.countAllByPhone(entity.getPhone()) > 0) {
            throw new BadRequestException("电话已存在!");
        }
        // 邮箱
        if (userProperties.getPhoneUnique() && StrUtil.isNotBlank(entity.getEmail()) && baseRepository.countAllByEmail(entity.getEmail()) > 0) {
            throw new BadRequestException("邮箱已经存在！");
        }
        // 初始化生成密码
        String password = passwordGeneratorManager.generate(entity.getPassword());
        // 给密码加密
        entity.setPassword(passwordEncoder.encode(password));
        entity.setIsFreeze(false);
    }


    @Override
    public void updateBefore(User entity, User resource) {
        // username
        if (!resource.getUsername().equals(entity.getPhone()) && baseRepository.countAllByUsername(resource.getUsername()) > 0) {
            throw new BadRequestException("用户名已存在!");
        }
        // phone
        if (resource.getPhone()!=null && userProperties.getPhoneUnique()){
            if (!resource.getPhone().equals(entity.getPhone()) && baseRepository.countAllByPhone(resource.getPhone()) > 0) {
                throw new BadRequestException("电话已存在!");
            }
        }
        // email
        if (resource.getEmail()!=null && userProperties.getEmailUnique()){
            if (!resource.getPhone().equals(entity.getPhone()) && baseRepository.countAllByEmail(resource.getEmail()) > 0) {
                throw new BadRequestException("邮箱已存在!");
            }
        }
    }

    @Override
    public void updateProperty(User entity, User resource) {
        entity.setNickname(resource.getNickname())
                .setDept(resource.getDept())
                .setPhone(resource.getPhone())
                .setEmail(resource.getEmail())
                .setIsFreeze(resource.getIsFreeze())
                .setEnabled(resource.getEnabled())
                .setExtra(resource.getExtra())
                // 更新角色信息
                .setRoles(new HashSet<>(resource.getRoles()));
    }

    @Override
    public Optional<UserDto> getByUsername(String username) {
        return baseRepository.findByUsername(username).map(baseMapstruct::toDto);
    }

    @Override
    public Optional<UserDto> getByEmail(String email) {
        return baseRepository.findByEmail(email).map(baseMapstruct::toDto);
    }

    @Override
    public Set<String> getUserPermissions(Long id) {
        User user = this.findByIdOrThrow(id);
        List<Long> roleIds = user.getRoles().stream().map(Role::getId).collect(Collectors.toList());
        List<MenuVo> menuVos = menuService.getMenuTreeByRoles(roleIds, null);
        // 汇集权限
        HashSet<String> res = new HashSet<>();
        user.getRoles().stream().map(Role::getPermission).forEach(res::add);
        TreeUtil.collectTreeProperty(menuVos, MenuVo::getChildren, MenuVo::getPermission, res);
        return res;
    }

    @Override
    public UserDto setUserRoles(Long id, List<Long> roleIds) {
        User user = this.findByIdOrThrow(id);
        List<Role> roles = roleRepository.findAllById(roleIds);
        if (roles.size() != roleIds.size()) {
            throw new BadRequestException("选择角色的数据异常！");
        }
        user.setRoles(new HashSet<>(roles));
        this.baseRepository.save(user);
        return baseMapstruct.toDto(user);
    }

    @Override
    public UserDto register(User user, String passwordGenerator) {
        // username
        if (baseRepository.countAllByUsername(user.getUsername()) > 0) {
            throw new BadRequestException("用户名已存在!");
        }
        if (userProperties.getEmailUnique() && StrUtil.isNotBlank(user.getEmail()) && baseRepository.countAllByEmail(user.getEmail()) > 0) {
            throw new BadRequestException("邮箱已经存在！");
        }
        if (userProperties.getPhoneUnique() && StrUtil.isNotBlank(user.getPhone()) && baseRepository.countAllByPhone(user.getPhone()) > 0) {
            throw new BadRequestException("手机号已经存在！");
        }
        // 生成密码
        String password = passwordGeneratorManager.generate(passwordGenerator, user.getPassword());
        user.setPassword(passwordEncoder.encode(password));
        user = this.baseRepository.save(user);
        UserDto userDto = baseMapstruct.toDto(user);
        // 返回密码
        userDto.setPassword(password);
        return userDto;
    }
}
